/*******************************************************************************
*
* Copyright 2011-2014 Spiffy UI Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.spiffyui.spsample.client;
import java.util.HashMap;
import org.spiffyui.client.JSDateUtil;
import org.spiffyui.client.JSEffectCallback;
import org.spiffyui.client.JSUtil;
import org.spiffyui.client.MainFooter;
import org.spiffyui.client.MessageUtil;
import org.spiffyui.client.nav.MainNavBar;
import org.spiffyui.client.nav.NavBarListener;
import org.spiffyui.client.nav.NavItem;
import org.spiffyui.client.nav.NavSection;
import org.spiffyui.client.nav.NavSeparator;
import org.spiffyui.client.rest.RESTException;
import org.spiffyui.client.rest.RESTLoginCallBack;
import org.spiffyui.client.rest.RESTObjectCallBack;
import org.spiffyui.client.rest.RESTility;
import org.spiffyui.spsample.client.i18n.SpiffyRsrc;
import org.spiffyui.spsample.client.rest.SampleAuthUtil;
import org.spiffyui.spsample.client.rest.VersionInfo;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.RootPanel;
/**
* This class is the main entry point for our GWT module.
*/
public class Index implements EntryPoint, NavBarListener, RESTLoginCallBack
{
private static final SpiffyRsrc STRINGS = (SpiffyRsrc) GWT.create(SpiffyRsrc.class);
private static Index g_index;
private SPSampleHeader m_header;
private MainNavBar m_navBar;
private MainFooter m_footer;
private final HashMap<NavItem, ComplexPanel> m_panels = new HashMap<NavItem, ComplexPanel>();
private boolean m_isVisible = false;
private boolean m_isSausage = false;
/** NavItem ID for Landing */
public static final String LANDING_NAV_ITEM_ID = "landing";
/** NavItem ID for Overview */
public static final String OVERVIEW_NAV_ITEM_ID = "overview";
/** NavItem ID for Get Started Intro*/
public static final String GET_STARTED_NAV_ITEM_ID = "getStarted";
/** NavItem ID for Samples panel*/
public static final String SAMPLES_NAV_ITEM_ID = "samples";
/** NavItem ID for Hosted Mode */
public static final String HOSTED_MODE_NAV_ITEM_ID = "hostedMode";
/** NavItem ID for CSS */
public static final String CSS_NAV_ITEM_ID = "css";
/** NavItem ID for Build */
public static final String SPEED_NAV_ITEM_ID = "speed";
/** NavItem ID for REST */
public static final String REST_NAV_ITEM_ID = "rest";
/** NavItem ID for Help */
public static final String HELP_NAV_ITEM_ID = "help";
/** NavItem ID for Get Involved */
public static final String GET_INVOLVED_NAV_ITEM_ID = "getInvolved";
/** NavItem ID for Authentication */
public static final String AUTH_NAV_ITEM_ID = "auth";
/** NavItem ID for Dates */
public static final String DATES_NAV_ITEM_ID = "l10n";
/** NavItem ID for Form */
public static final String FORM_NAV_ITEM_ID = "forms";
/** NavItem ID for Widgets */
public static final String WIDGETS_NAV_ITEM_ID = "widgets";
/** NavItem ID for Auth Test */
public static final String AUTH_TEST_NAV_ITEM_ID = "authTest";
/** NavItem ID for License */
public static final String LICENSE_NAV_ITEM_ID = "license";
/**
* The Index page constructor
*/
public Index()
{
g_index = this;
bindJavaScript();
}
/**
* Get the message bundle object for this application
*
* @return the message bundle
*/
public static final SpiffyRsrc getStrings()
{
return STRINGS;
}
@Override
public void onModuleLoad()
{
DOM.getElementById("main").addClassName("landing");
/*
We are setting a custom authentication provider. Custom authentication
providers can override the UI of the login or provide access to totally
new authentication mechanisms. Ours just overrides a string in the
login panel
*/
SampleAuthUtil auth = new SampleAuthUtil();
RESTility.setAuthProvider(auth);
RESTility.setOAuthProvider(auth);
m_header = new SPSampleHeader();
Anchor title = new Anchor(getStrings().mainTitle(), "#");
m_header.addHeaderTitleWidget(title);
title.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event)
{
event.preventDefault();
selectItem(LANDING_NAV_ITEM_ID);
}
});
InlineLabel subtitle = new InlineLabel(getStrings().mainSubtitle());
m_header.addHeaderTitleWidget(subtitle);
subtitle.getElement().setId("mainsubtitle");
m_footer = new MainFooter();
loadFooter();
m_navBar = new MainNavBar();
if (isRunningUnitTests()) {
loadUnitTests();
return;
}
/*
The landing panel
*/
NavItem item = new NavItem(LANDING_NAV_ITEM_ID, getStrings().landing(),
getStrings().landing_tt(), generateNavItemURL(LANDING_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new LandingPanel());
/*
The overview panel
*/
item = new NavItem(OVERVIEW_NAV_ITEM_ID, getStrings().overview(),
getStrings().overview_tt(), generateNavItemURL(OVERVIEW_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new OverviewPanel());
/*
The Getting started panels
*/
item = new NavItem(GET_STARTED_NAV_ITEM_ID, getStrings().getStarted(),
getStrings().getStarted_tt(), generateNavItemURL(GET_STARTED_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new GetStartedPanel());
/*
The samples panels
*/
item = new NavItem(SAMPLES_NAV_ITEM_ID, getStrings().samples(),
getStrings().samples_tt(), generateNavItemURL(SAMPLES_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new SamplesPanel());
/*
* Collapsible Features Nav Section
*/
NavSection featureSection = new NavSection("featuresNavSection", getStrings().features());
featureSection.setTitle(getStrings().features_tt());
m_navBar.add(featureSection);
/*
The CSS panel
*/
item = new NavItem(CSS_NAV_ITEM_ID, getStrings().css(), getStrings().css_tt(), generateNavItemURL(CSS_NAV_ITEM_ID));
featureSection.add(item);
m_panels.put(item, new CSSPanel());
/*
The date info panel
*/
item = new NavItem(DATES_NAV_ITEM_ID, getStrings().l10n(), getStrings().l10n_tt(), generateNavItemURL(DATES_NAV_ITEM_ID));
featureSection.add(item);
m_panels.put(item, new DatePanel());
/*
The rest info panel
*/
item = new NavItem(REST_NAV_ITEM_ID, getStrings().restTitle(),
getStrings().restTitle_tt(), generateNavItemURL(REST_NAV_ITEM_ID));
featureSection.add(item);
m_panels.put(item, new RESTPanel());
/*
The authentication info panel
*/
item = new NavItem(AUTH_NAV_ITEM_ID, getStrings().auth(),
getStrings().auth_tt(), generateNavItemURL(AUTH_NAV_ITEM_ID));
featureSection.add(item);
m_panels.put(item, new AuthPanel());
/*
The build info panel
*/
item = new NavItem(SPEED_NAV_ITEM_ID, getStrings().speed(),
getStrings().speed_tt(), generateNavItemURL(SPEED_NAV_ITEM_ID));
featureSection.add(item);
m_panels.put(item, new BuildPanel());
addSamplePanels();
/*
A separator
*/
m_navBar.add(new NavSeparator(HTMLPanel.createUniqueId()));
addDocPanels();
m_navBar.addListener(this);
m_navBar.setBookmarkable(true);
selectDefaultItem();
RESTility.addLoginListener(this);
addBackToTop();
}
private void selectDefaultItem()
{
/*
* If the user has loaded this application in their
* current session then we want to bring them back to the
* same page when the application reloads.
*/
if (getIdFromHash() != null &&
m_navBar.getItem(getIdFromHash()) != null) {
/*
* Then there is a hash from the history in the new form
*/
m_navBar.selectItem(m_navBar.getItem(getIdFromHash()));
itemSelected(m_navBar.getItem(getIdFromHash()));
} else if (Window.Location.getHref().indexOf('?') > -1 &&
m_navBar.getItem(getIdFromParameter()) != null) {
/*
* Then there is a parameter from the URL in an HTML5 browser.
*/
m_navBar.selectItem(m_navBar.getItem(getIdFromParameter()));
itemSelected(m_navBar.getItem(getIdFromParameter()));
} else if (Window.Location.getHash() != null &&
Window.Location.getHash().length() > 0 &&
m_navBar.getItem(Window.Location.getHash().substring(2)) != null) {
/*
* Then there is a hash from sitemap
*/
m_navBar.selectItem(m_navBar.getItem(Window.Location.getHash().substring(2)));
itemSelected(m_navBar.getItem(Window.Location.getHash().substring(2)));
} else if (Window.Location.getHash() != null &&
Window.Location.getHash().length() > 0 &&
m_navBar.getItem(Window.Location.getHash().substring(4)) != null) {
/*
* Then there is a hash from the history in the old form
*/
m_navBar.selectItem(m_navBar.getItem(Window.Location.getHash().substring(4)));
itemSelected(m_navBar.getItem(Window.Location.getHash().substring(4)));
} else {
m_navBar.selectItem(m_navBar.getItem(LANDING_NAV_ITEM_ID));
}
}
private static String getIdFromParameter()
{
int qIndex = Window.Location.getHref().indexOf('?');
int aIndex = Window.Location.getHref().indexOf('&');
if (qIndex > -1) {
if (aIndex > -1) {
return Window.Location.getHref().substring(qIndex + 1, aIndex);
} else {
return Window.Location.getHref().substring(qIndex + 1);
}
} else {
return null;
}
}
private static String getIdFromHash()
{
if (Window.Location.getHash().length() < 2) {
return null;
}
String hash = Window.Location.getHash();
/*
In HTML4 browsers the hash will look like this: #?landing&_suid=777
*/
int start = 2;
int end = hash.indexOf('&');
return hash.substring(start, end);
}
/**
* Generate a URL based on the NavItem ID
* @param id - the ID of the NavItem
* @return the URL to the NavItem
*/
public static String generateNavItemURL(String id)
{
return Window.Location.createUrlBuilder().setHash("?" + id).buildString();
}
private void loadUnitTests()
{
UnitTestPanel.runUnitTests();
}
private void addSamplePanels()
{
/*
* Collapsible Features Nav Section
*/
NavSection demoSection = new NavSection("demoNavSection", getStrings().demos());
demoSection.setTitle(getStrings().demos_tt());
m_navBar.add(demoSection);
/*
The sample form panel
*/
NavItem item = new NavItem(FORM_NAV_ITEM_ID, getStrings().form(), getStrings().form_tt(), generateNavItemURL(FORM_NAV_ITEM_ID));
demoSection.add(item);
m_panels.put(item, new FormPanel());
/*
The sample widgets panel
*/
item = new NavItem(WIDGETS_NAV_ITEM_ID, getStrings().widgets(),
getStrings().widgets_tt(), generateNavItemURL(WIDGETS_NAV_ITEM_ID));
demoSection.add(item);
m_panels.put(item, new WidgetsPanel());
/*
The auth test panel
*/
item = new NavItem(AUTH_TEST_NAV_ITEM_ID, getStrings().login(),
getStrings().login_tt(), generateNavItemURL(AUTH_TEST_NAV_ITEM_ID));
demoSection.add(item);
m_panels.put(item, new AuthTestPanel());
}
private void addDocPanels()
{
NavItem item = new NavItem(HOSTED_MODE_NAV_ITEM_ID, getStrings().devMode(),
getStrings().devMode_tt(), generateNavItemURL(HOSTED_MODE_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new HostedModePanel());
/*
The get involved panel
*/
item = new NavItem(GET_INVOLVED_NAV_ITEM_ID, getStrings().getInvolved(),
getStrings().getInvolved_tt(), generateNavItemURL(GET_INVOLVED_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new GetInvolvedPanel());
/*
The help panel
*/
item = new NavItem(HELP_NAV_ITEM_ID, getStrings().help(), getStrings().help_tt(), generateNavItemURL(HELP_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new HelpPanel());
/*
The license panel
*/
item = new NavItem(LICENSE_NAV_ITEM_ID, getStrings().lic(), getStrings().lic_tt(), generateNavItemURL(LICENSE_NAV_ITEM_ID));
m_navBar.add(item);
m_panels.put(item, new LicensePanel());
}
@Override
public boolean preItemSelected(NavItem item)
{
return true;
}
@Override
public void itemSelected(NavItem item)
{
if (item.getId().equals(LANDING_NAV_ITEM_ID)) {
DOM.getElementById("main").addClassName("landing");
} else {
DOM.getElementById("main").removeClassName("landing");
}
if (!m_isSausage) {
for (final NavItem key : m_panels.keySet()) {
/*
We could hide and show these panels by just calling setVisible,
but that causes a redraw bug in IE 8 where the body extends for
for the total height of the page below the footer.
*/
if (key.equals(item)) {
/*
* The sliding grid on the widgets panel needs to be aligned
* when it is set to visible, otherwise if the landing page
* is the first page the browser's been to, the onLoad is too early
* for the width of the sliding grid panel to be determined.
*/
if (WIDGETS_NAV_ITEM_ID.equals(key.getElement().getId())) {
JSUtil.show("#" + m_panels.get(key).getElement().getId(), "normal", new JSEffectCallback() {
@Override
public void effectComplete(String id)
{
((WidgetsPanel) m_panels.get(key)).getSlideGridPanel().reAlignGrid();
}
});
} else {
JSUtil.show("#" + m_panels.get(key).getElement().getId());
}
} else {
JSUtil.hide("#" + m_panels.get(key).getElement().getId(), "fast");
}
}
Window.scrollTo(0, 0);
} else {
for (NavItem key : m_panels.keySet()) {
/*
In the sausage mode we just scroll things to be visible
*/
if (key.equals(item)) {
Window.scrollTo(0, getTop(m_panels.get(key).getElement().getId()));
return;
}
}
}
}
/**
* Set the sausage mode.
*
* @param sausage true if we are showing the sausage menu and false otherwise
*/
public static void setSausageMode(boolean sausage)
{
g_index.m_isSausage = sausage;
}
private native int getTop(String id) /*-{
return $wnd.$('#' + id).position().top;
}-*/;
/**
* Select the NavItem
* @param itemId - ID of the NavItem to select
*/
public static void selectItem(String itemId)
{
NavItem item = g_index.m_navBar.getItem(itemId);
g_index.m_navBar.selectItem(item);
}
private void loadFooter()
{
try {
VersionInfo.getVersionInfo(new RESTObjectCallBack<VersionInfo>() {
public void success(VersionInfo info)
{
m_footer.setFooterString(getStrings().footer(info.getVersion(),
JSDateUtil.getDate(info.getDate())));
}
public void error(String message)
{
MessageUtil.showFatalError(message);
}
public void error(RESTException e)
{
MessageUtil.showFatalError(e.getReason());
}
});
} catch (Exception e) {
/*
This sometimes happens when you are running Spiffy on a machine that also
runs Sentinel.
*/
}
}
/**
* Sets the application visible once we have logged in.
* People worry about security when they see controls
* before we've logged in.
*/
public static void showApplication()
{
if (!g_index.m_isVisible) {
JSUtil.show("#mainFooter", "fast");
JSUtil.show("body > #mainWrap > #main", "fast");
g_index.m_isVisible = true;
}
}
/**
* Sets the application invisible. This is called by the
* authentication framework to hide the UI when we need
* to show the login dialog again.
*/
public static void hideApplication()
{
if (g_index.m_isVisible) {
JSUtil.hide("#mainFooter", "fast");
JSUtil.hide("body > #mainWrap > #main", "fast");
g_index.m_isVisible = false;
}
}
/**
* This is a utility method to get the name of the current logged in
* user and apply it to the main header of the application.
*
*/
public static void updateMainHeader()
{
if (RESTility.getUserToken() == null) {
return;
}
String token = RESTility.getUserToken();
String name = token.substring(0, token.indexOf('-'));
g_index.m_header.setWelcomeString(getStrings().welcome(name));
JSUtil.show("#header_logout", "fast");
}
/**
* returns whether the user is logged in or not
* @return true if the user is logged in (browser cookie is there)
*/
public static boolean userLoggedIn()
{
String userToken = RESTility.getUserToken();
if ((userToken == null) || (userToken.length() <= 0)) {
return false;
}
return true;
}
/**
* When login is successful, make the application visible
*/
public void onLoginSuccess()
{
Index.showApplication();
updateMainHeader();
updateAuthTestButton();
updateLoginWidgetButton();
}
/**
* do nothing
*/
public void loginPrompt()
{
//no-op
}
private void updateAuthTestButton()
{
JSUtil.setText("authTestBtn", getStrings().secData());
}
private void updateLoginWidgetButton()
{
JSUtil.setText("doLoginBtn", getStrings().secData());
}
/**
* We host the sample of this application on Google App Engine. It will run almost everything
* we need, but it can't handle the HTTP connections we need for authentication. We change
* our UI in that case so it doesn't look broken.
*
* @return true if we are running on Google App Engine and false otherwise
*/
public static boolean isAppEngine()
{
return Window.Location.getHref().startsWith("http://spiffyui.appspot.com") ||
Window.Location.getHref().startsWith("http://spiffyui-staging.appspot.com") ||
Window.Location.getHref().startsWith("http://spiffyui.org") ||
Window.Location.getHref().startsWith("http://www.spiffyui.org");
}
/**
* This method indicates if the SPSample is running in unit test mode.
*
* @return true if it is running in unit test mode and false otherwise
*/
public static native boolean isRunningUnitTests() /*-{
if ($wnd.spiffyui.runUnitTests) {
return true;
} else {
return false;
}
}-*/;
private void addBackToTop()
{
Anchor a = new Anchor(getStrings().backToTop(), "#");
a.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event)
{
scrollTo(0);
event.preventDefault();
}
});
a.getElement().setId("backToTop");
RootPanel.get("mainContent").add(a);
}
/**
* Call addToc with default cssSelector of h2
*
* @param panel - the HTMLPanel that contains h2 tags and a toc div with a ul in it.
* The ul must have an id equal to the HTMLPanel's id + "TocUl".
*/
public static void addToc(HTMLPanel panel)
{
addToc(panel, "h2");
}
/**
* Add the table of contents to HTMLPanel for every cssSelector tag with an id attribute. The inner text of
* the css selector's tag will be used as the display name for the table of contents list item
* unless there is a title attribute for it, in which case it will use the title.
*
* @param panel - the HTMLPanel that contains cssSelector and a toc div with a ul in it.
* @param cssSelector - the selector under the panel's id to determine what creates the table of contents
* The ul must have an id equal to the HTMLPanel's id + "TocUl".
*/
public static void addToc(HTMLPanel panel, String cssSelector)
{
String panelId = panel.getElement().getId();
addTocListItems(panel, panelId, cssSelector);
}
private static void addTocLi(final HTMLPanel panel, final String liDisplay, final String h2Id, final String liId)
{
Anchor anchor = new Anchor(liDisplay, "#");
anchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event)
{
Index.scrollTo(h2Id);
event.preventDefault();
}
});
if (panel.getElementById(liId) != null) {
panel.add(anchor, liId);
}
}
private static native void addTocListItems(HTMLPanel panel, String panelId, String cssSelector) /*-{
var ul = $wnd.$("#" + panelId + "TocUl");
$wnd.$("#" + panelId + " " + cssSelector).each(function() {
var thiz = $wnd.$(this);
var display = thiz.attr("title") ? thiz.attr("title") : thiz.html();
var h2Id = thiz.attr("id");
if (!h2Id) {
//If there is no id, it will not allow scrolling to it. Intentionally leave out this attribute to skip adding it to TOC
return;
}
var liId = "li" + h2Id;
ul.append("<li id=\"" + liId + "\"></li>");
@org.spiffyui.spsample.client.Index::addTocLi(Lcom/google/gwt/user/client/ui/HTMLPanel;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)(panel, display, h2Id, liId);
});
}-*/;
/**
* Scroll to the id using animation
* @param id to scroll to
*/
public static native void scrollTo(String id) /*-{
$wnd.$("html,body").animate({scrollTop: $wnd.$("#" + id).offset().top}, "slow");
}-*/;
/**
* Scroll to the y position using animation
* @param y coordinate
*/
public static native void scrollTo(int y) /*-{
$wnd.$("html,body").animate({scrollTop: y}, "slow");
}-*/;
private static native void bindJavaScript() /*-{
$wnd.spsample.shouldShowTopLink = function(id) {
return @org.spiffyui.spsample.client.Index::shouldShowTopLink()();
}
}-*/;
private static boolean shouldShowTopLink()
{
if (g_index.m_navBar.getSelectedItem() == null) {
return false;
} else {
return !g_index.m_navBar.getSelectedItem().getId().equals(LANDING_NAV_ITEM_ID);
}
}
}